<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <style>
    html {
      overflow: hidden;
    }

    body {
      margin: 0;
    }

    #cas {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      z-index: 2;
      display: block;
      margin: auto;
    }

    #bgcas {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      background-color: #CCC;
      display: block;
      margin: auto;
    }
  </style>
  <title>粒子时钟</title>
</head>
<body>
<canvas id="cas" width="1400" height="600">抱歉，您的浏览器不支持canvas</canvas>
<canvas id="bgcas" width="1400" height="600"></canvas>
<script>
  var numData = [
    "1111/1001/1001/1001/1001/1001/1111", //0
    "0001/0001/0001/0001/0001/0001/0001", //1
    "1111/0001/0001/1111/1000/1000/1111", //2
    "1111/0001/0001/1111/0001/0001/1111", //3
    "1001/1001/1001/1111/0001/0001/0001", //4
    "1111/1000/1000/1111/0001/0001/1111", //5
    "1111/1000/1000/1111/1001/1001/1111", //6
    "1111/0001/0001/0001/0001/0001/0001", //7
    "1111/1001/1001/1111/1001/1001/1111", //8
    "1111/1001/1001/1111/0001/0001/1111", //9
    "0000/0000/0010/0000/0010/0000/0000" //:
  ]

  var canvas = document.getElementById("cas"),
      bgcanvas = document.getElementById("bgcas"),
      ctx = canvas.getContext('2d'),
      bgctx = bgcanvas.getContext("2d");

  bgcanvas.width = canvas.width;
  bgcanvas.height = canvas.height;

  var P_radius = 6, Gravity = 9.8;
  var Particle = function() {
    this.x = 0;
    this.y = 0;
    this.vx = 0;
    this.vy = 0;
    this.color = "";
    this.visible = false;
    this.drop = false;
  }
  var lastColor;
  Particle.prototype = {
    constructors: Particle,
    paint: function() {		//绘制自身
      if (lastColor !== this.color) {
        ctx.fillStyle = lastColor = this.color;
      }
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
      ctx.fill();
    },
    reset: function(x, y, color) {		//重置
      this.x = x;
      this.y = y;
      this.vx = 0;
      this.vy = 0;
      this.color = color;
      this.visible = true;
      this.drop = false;
      this.radius = 0;
    },
    isDrop: function() {		//落下
      this.drop = true;
      var vx = Math.random() * 15 + 10
      var vy = -Math.random() * 50 + 10;
      this.vx = Math.random() >= 0.5 ? -vx : vx;
      this.vy = vy;
    },
    update: function(time) {		//每一帧的动作
      if (this.drop) {
        this.x += this.vx * time;
        this.y += this.vy * time;

        var vy = this.vy + Gravity * time;

        if (this.y >= canvas.height - P_radius) {
          this.y = canvas.height - P_radius
          vy = -vy * 0.8;
        }

        this.vy = vy;

        if (this.x < -P_radius || this.x > canvas.width + P_radius || this.y > canvas.height + P_radius) {
          this.visible = false;
        }
      }

      if (this.radius < P_radius) {
        this.radius += 0.5;
      }
    }
  }

  window.RAF = (function() {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
          window.setTimeout(callback, 1000 / 60);
        };
  })();

  var timeCount_0 = 0, timeCount_1 = 0, particles = [];
  function initAnimate() {
    for (var i = 0; i < 200; i++) {
      var p = new Particle();
      particles.push(p);
    }

    timeCount_0 = new Date();
    timeCount_1 = new Date();

    drawBg();

    setTime(timeCount_0);
    animate();
  }

  function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    var timeCount_2 = new Date();

    if (timeCount_1 - timeCount_0 >= 1000) {
      setTime(timeCount_1);
      timeCount_0 = timeCount_1;
    }

    particles.forEach(function(p) {
      if (p.visible) {
        p.update(16 / 70);
        p.paint();
      }
    });

    timeCount_1 = timeCount_2;

    RAF(animate)
  }


  var X_J = 10;       //圆点间距
  var xjg = 20;       //各个字母之间的距离
  var yjg = 22;       //每一栏上下距离
  function drawBg() {
    var tx = (canvas.width - ((P_radius * 2 + X_J) * 4 * 8 + 7 * xjg)) / 2;
    for (var i = 0; i < 8; i++) {
      var ty = (canvas.height - ((P_radius + yjg) * 6)) / 2;
      for (var j = 0; j < numData[0].length; j++) {
        var tt = numData[0].charAt(j);
        if (tt === "/") {
          ty += yjg;
        } else if (tt == 1 || tt == 0) {
          var x = tx + j % 5 * (P_radius * 2 + X_J),
              y = ty;
          bgctx.beginPath();
          bgctx.arc(x, y, P_radius, 0, 2 * Math.PI);
          bgctx.fillStyle = "#FFF";
          bgctx.fill();
        }
      }
      tx += xjg + 4 * (P_radius * 2 + X_J);
    }
  }

  var lastDate;
  function setTime(time) {
    var h = time.getHours() + "",
        m = time.getMinutes() + "",
        s = time.getSeconds() + "";
    h = h.length === 1 ? "0" + h : h;
    m = m.length === 1 ? "0" + m : m;
    s = s.length === 1 ? "0" + s : s;

    var nowdate = h + ":" + m + ":" + s;

    var color = "";
    var i = 0;

//        跟上一次的时间进行比较，获取改变的字符点
    if (lastDate) {
      for (var k = 0; k < nowdate.length; k++) {
        if (lastDate.charAt(k) !== nowdate.charAt(k)) {
          i = k;
          break;
        }
      }
    }
    lastDate = nowdate;

    var tx = (canvas.width - ((P_radius * 2 + X_J) * 4 * 8 + 7 * xjg)) / 2; //计算时间的x轴值
    var ty = (canvas.height - ((P_radius + yjg) * 6)) / 2;  //计算时间的y轴值
//        遍历时间字符
    for (; i < nowdate.length; i++) {
      var charX = tx + i * (xjg + 4 * (P_radius * 2 + X_J));   //计算该字符的X轴值
      var charY = ty;

      var timeChar = nowdate.charAt(i);
      var text = numData[timeChar === ":" ? 10 : +timeChar];     //获取该字符的映射表

      if (i < 3) {
        color = "#279548";
      } else if (i < 6) {
        color = "#D65050";
      } else {
        color = "#797C17";
      }

      for (var j = 0; j < text.length; j++) {
        var tt = text.charAt(j);
        if (tt === "/") {
          charY += yjg;
        } else {
          var x = charX + j % 5 * (P_radius * 2 + X_J);
          var y = charY;
          var pp = null;
          var usefullp = null;

          for (var ref = 0; ref < particles.length; ref++) {
            var p = particles[ref];
            if (p.visible && p.x === x && p.y === y) {
              pp = p;
            } else if (!p.visible && !usefullp) {
              usefullp = p;
            }
          }

          if (pp && tt == 0) {
            pp.isDrop();
          } else if (!pp && tt == 1) {
            usefullp.reset(x, y, color);
          }
        }
      }
    }
  }

  initAnimate()
</script>
</body>
</html>